/*--------------------------------------------------------------------------------------------------
Yatak Operation System Project Copyright 2012-2013
Author : Ilya Churaev
Started : 20 Apr 2012		Updated : 21 Feb 2013
File Name : task.c		Purpose : multitasking
--------------------------------------------------------------------------------------------------*/
#include <yatak/paging.h>
#include <yatak/debug.h>
#include <yatak/kheap.h>
#include <yatak/yatak.h>
#include <yatak/tty.h>
#include <screen.h>
#include <types.h>
#include <sched.h>
#include <users.h>
#include <list.h>

//#define DEBUG_TASK

#ifndef DEBUG_TASK
#define dprintf
#define DBGPRINTF(s)
#endif

//running task
volatile task_t *current_task = NULL;
//linked list
volatile task_t *ready_queue = NULL;

// Некоторые внешние ссылки, для доступа к объектам в файле paging.c
extern page_directory_t *kernel_directory;
extern page_directory_t *current_directory;
extern void alloc_frame(page_t*,int,int);
extern uint32 initial_esp;
extern uint32 read_eip();
extern void move_to_pl3();

// List for free pid
pid_list_t * free_pid = NULL;
// Next PID
uint32 next_pid = START_USERS_PID;

// Linking new task
int LinkTask (task_t * newtask);
// Unlinking task
int UnlinkTask (task_t *task);

static void DeleteTask (task_t * task);

pid_t pop_pid ()
{
	if (free_pid)
	{
		pid_t pid = free_pid->pid;
		pid_list_t * tmp = free_pid;
		free_pid = free_pid->next;
		kfree (tmp);
		return pid;
	}
	return (pid_t)0;
}

void push_pid (pid_t * pid)
{
	pid_list_t * tmp = free_pid;
	while (tmp)
	{
		if (tmp->pid == pid)
			return; 
		tmp = tmp->next;
	}
	tmp = (pid_list_t *)kmalloc (sizeof (pid_list_t));
	tmp->pid = pid;
	tmp->next = free_pid;
	free_pid = tmp;
}

// Function for getting new id
pid_t GetFreeID ()
{
	pid_t pid = (pid_t) pop_pid();
	if (pid > START_USERS_PID)
		return pid;
	return next_pid++;
}

static thread_t * CreateThreadStruct (task_t * parent, tid_t id, int prio)
{
	unsigned int flags;
	save_flags (flags);
	cli();
	
	if (!parent)
	{
		dprintf ("[CreateThreadStruct] Error! parent = NULL...\n");
		return NULL;
	}
	
	dprintf ("[CreateThreadStruct] thread_t has size: %d...\n",sizeof(thread_t));
	thread_t * newthread = (thread_t*)kmalloc(sizeof(thread_t));
	newthread->tid = id;
	newthread->msgbuf = NULL;
	newthread->prio = newthread->static_prio = prio;
	newthread->eip = 0;
	newthread->next = NULL;
	newthread->parent = parent;
	newthread->state = TASK_SLEEPING;
	newthread->terminal = NULL;
	
	restore_flags (flags);
	
	return newthread;
}

//Create struct for new task
task_t * CreateTaskStruct (char * name, pid_t id, page_directory_t * directory, int prio)
{
	unsigned int flags;
	save_flags (flags);
	cli();
	dprintf ("[CreateTaskStruct] task_t has size: %d...\n",sizeof(task_t));
	task_t * newtask = (task_t*)kmalloc(sizeof(task_t));
	if (!strcmp (name, "NULL"))
	{
		sprintf (name,"%d\0",id);
		strcpy (newtask->name, name);
	}
	else if (!strcmp (name, "\0") || name == NULL)
		strcpy (newtask->name, "Unknown");
	else
		strcpy (newtask->name, name);
	newtask->pid = id;
	newtask->flags = PF_STARTING;
	
	//Create thread	
	newtask->threads = CreateThreadStruct (newtask, (tid_t) id, prio);
	newtask->cur_thread = newtask->threads;
	newtask->state = newtask->threads->state = TASK_SLEEPING;
	//newtask->exit_state = newtask->exit_state = newtask->exit_code = 0;
	newtask->brother = NULL;
	newtask->parent = NULL;
	newtask->child = NULL;
	newtask->next = NULL;
	newtask->uid = GetUserId ();
	//dprintf ("[CreateTaskStruct] directory = 0x%x\n",directory);
	newtask->page_directory = directory;
	
	newtask->esp = newtask->ebp = 0;
	
	//newtask->threads = CreateThreadStruct (id, newtask, stack_size, prio);
	restore_flags (flags);
	
	return newtask;
}

void init_tasking()
{
	unsigned int flags;
	save_flags (flags);
	cli();
	// Перемещаю стек так, что бы знать, где он находится
	dprintf ("[init_tasking] the stack in the size 0x2000 relocate to 0x10000000...\n");
	move_stack((void*)0x10000000, 0x2000);
	
	// Инициализирую первую задачу task (задачу ядра)
	dprintf ("[init_tasking] initialising the first task (kernel task)...\n");
	
	dprintf ("[init_tasking] current_directory = 0x%x\n",current_directory);
	ready_queue = current_task = CreateTaskStruct ("Init\0",GetFreeID (),current_directory,PRIO_NORMAL);
	
	create_tty (current_task->cur_thread,NULL);
	
	restore_flags (flags);
	//sti();
	MessageFoS ("Initialising Task Manager\t\t\t\t\t",true);
}

void move_stack(void *new_stack_start, uint32 size)
{
	uint32 i;
	// Выделяем немного места для нового стека
	for( i = (uint32)new_stack_start; i >= ((uint32)new_stack_start-size); i -= PAGE_SIZE)
	{
		// Стек общего назначения исползется в пользовательском режиме
		alloc_frame( get_page(i, 1, current_directory), 0 /* User mode */, 1 /* Is writable */);
	}
	
	// Обновление TLB выполняется с помощью чтения и повторной записи адреса директория страниц
	uint32 pd_addr;
	asm volatile ("mov %%cr3, %0" : "=r" (pd_addr));
	asm volatile ("mov %0, %%cr3" : : "r" (pd_addr));

	// Старые ESP и EBP читаем из регистров
	uint32 old_stack_pointer;
	uint32 old_base_pointer;
	asm volatile ("mov %%esp, %0" : "=r" (old_stack_pointer));
	asm volatile ("mov %%ebp, %0" : "=r" (old_base_pointer));

	// Offset to add to old stack addresses to get a new stack address.
	uint32 offset = (uint32)new_stack_start - initial_esp;

	// New ESP and EBP.
	uint32 new_stack_pointer = old_stack_pointer + offset;
	uint32 new_base_pointer = old_base_pointer + offset;

	// Копирование стека
	memcpy((void*)new_stack_pointer, (void*)old_stack_pointer, initial_esp-old_stack_pointer);

	// Проходим по исходному стеку и копируем новые значения в новый стек  
	for(i = (uint32)new_stack_start; i > (uint32)new_stack_start-size; i -= 4)
	{
		uint32 tmp = * (uint32*)i;
		// Если значение tmp попадает в диапазон адресов старого стека, полагаю, что это указатель базы
		// и переопределяю его. В результате к сожалению, будет переопределенно ЛЮБОЕ значение в этом
		// диапазоне независимо от того, являетя оно указателем базы или нет
		if (( old_stack_pointer < tmp) && (tmp < initial_esp))
		{
			tmp = tmp + offset;
			uint32 *tmp2 = (uint32*)i;
			*tmp2 = tmp;
		}
	}

	// Замена стеков
	asm volatile ("mov %0, %%esp" : : "r" (new_stack_pointer));
	asm volatile ("mov %0, %%ebp" : : "r" (new_base_pointer));
}

int fork()
{
	// Я модифицирую структуру ядра и необходимо отключить прерывания
	unsigned int flags;
	save_flags (flags);
	cli();
	
	// Беру указатель на структуру task этого процеса для последующего к ней обращения.
	task_t *parent_task = (task_t*)current_task;

	// Клонирую адресное пространство
	dprintf ("[fork] createng directory...\n");
	page_directory_t *directory = clone_directory(current_directory);

	// Создаю новый процесс
	task_t *new_task = CreateTaskStruct ("NULL",GetFreeID(), directory, PRIO_NORMAL);

	//add to linked list
	task_t *tmp_task = (task_t*)ready_queue;
	while (tmp_task->next)
		tmp_task = tmp_task->next;
	tmp_task->next = new_task;
    
	// Добавляю в древо процессов
	LinkTask (new_task);
	
	// This will be the entry point for the new process.
	uint32 eip = read_eip();
	
	// Проверка на родительский или доенрний процесс
	if (current_task == parent_task)
	{
		// Нахожусь в родительской задаче, поэтому настраиваю esp/ebp/eip для дочерней задачи
		uint32 esp;
		uint32 ebp;
		
		if (!new_task->cur_thread)
		{
			dprintf ("[fork] Error! Process has not main thread!\n");
			UnlinkTask (new_task);
			kfree (new_task);
			restore_flags (flags);
			return -1;
		}
        
		asm volatile ("mov %%esp, %0" : "=r"(esp));
		asm volatile ("mov %%ebp, %0" : "=r"(ebp));
		new_task->esp = esp;
		new_task->ebp = ebp;
		new_task->cur_thread->eip = eip;
        
		restore_flags (flags);
		return (int)new_task->pid;
	}
	else
	{
		// Нахожусь в дочерней задаче
		return 0;
	}
}

void set_task_priority (int prio)
{
	unsigned int flags;
	save_flags (flags);
	cli();
	current_task->cur_thread->prio = prio;
	restore_flags (flags);
}

int getpid()
{
	dprintf(" PID: %d ",current_task->pid);
	return current_task->pid;
}

int start_task(char * name, bool daemon,void (*func)(void*), int argc, void * argv)
{
	int ret = fork();
	if (!ret)
	{
		if (!strcmp (name, "NULL"))
		{
			sprintf (name,"%d\0",getpid ());
			strcpy (current_task->name, name);
		}
		else if (!strcmp (name, "\0") || name == NULL)
			strcpy (current_task->name, "Unknown");
		else
			strcpy (current_task->name, name);
		if (!daemon)
			create_tty (current_task->cur_thread,NULL);
		current_task->cur_thread->argc = argc;
		current_task->cur_thread->argv = argv;
		func(argv);
		exit();
		for(;;);
	}
	switch_task ();
	return ret;
}

int kill_task(int pid)
{
	uint32 flags;
	save_flags (flags);
	cli ();
	int is_current = 0;
	task_t *dtask = NULL;
	task_t *task = (task_t*)ready_queue;
	while (task)
	{
		if(task->pid == pid)
		{
			dtask = task;
			break;
		}
		task = task->next;
	}
	if(!dtask)
		return 1;
		
	if(UnlinkTask(dtask))
	{
		dprintf ("[kill_task] UnlinkTask return !0...\n");
		return 1;
	}
	
	if (dtask->pid == current_task->pid)
		is_current = 1;
	DeleteTask (dtask);
	if (is_current)
	{
		restore_flags (flags);
		switch_task ();
	}
	restore_flags (flags);
	//kfree((void*)task_r);
	return 0;
}

void exit() // I don't think this works
{
	unsigned int flags;
	save_flags (flags);
	cli();
	//Just incase the removal doesn't work 100%
	//we make sure we are using as little time as possible
	current_task->threads->prio = PRIO_DEAD;
	
	// We didn't find the task
	if(UnlinkTask(current_task))
	{
		dprintf ("[exit] UnlinkTask return !0...\n");
		return;
	}
	
	// Find previous task
	task_t *task_d = NULL;
	task_t *task_r = (task_t*)ready_queue;
	for(;task_r->next!=0;task_r=task_r->next)
	{
		if(task_r->next == current_task)
		{
			//We got the previous task
			task_d=task_r;
			//break; //Don't bother with the rest of the list
		}
	}
     
	// We didn't find the task
	if(!task_d)
		return;
	task_d->next = current_task->next;

	// Free the memory
	DeleteTask (current_task);
	//kfree((void*)current_task->cur_thread);
	//kfree((void*)current_task);
	
	restore_flags (flags);
	switch_task(); //Don't waste any time
}

int new_thread()
{
	unsigned int flags;
	save_flags (flags);
	cli();
	
	task_t * parent_task = (task_t*)current_task;
	thread_t * parent_thread = (thread_t*)parent_task->cur_thread;
	
	thread_t * new_thread = CreateThreadStruct (parent_task, (tid_t) GetFreeID(), parent_thread->prio);

	thread_t * tmp = parent_task->threads;
	while (tmp->next) tmp = tmp->next;
	
	tmp->next = new_thread;
	
	// entry point
	uint32 eip = read_eip();
	if (current_task == parent_task)
	{
		if (current_task->cur_thread == parent_thread)
		{
			uint32 esp;
			uint32 ebp;
			asm volatile ("mov %%esp, %0" : "=r"(esp));
			asm volatile ("mov %%ebp, %0" : "=r"(ebp));
			new_thread->eip = eip;
			restore_flags (flags);

			return new_thread->tid;
		}
	}
	return 0;
}

void switch_to_userland()
{
   /* We need to set up the stack to return execution in userland after an IRET. 
      We do this by loading the PL3 CS and SS into the registers, and then loading
      ESP, EIP and EFLAGS correctly. We then reenable interrupts using the IF flag
      in EFLAGS (as STI causes an exception in PL3) */
   /* asm volatile ("\
     mov $0x23, %ax; \
     mov %ax, %ds; \
     mov %ax, %es; \
     mov %ax, %fs; \
     mov %ax, %gs; \
     mov %esp, %eax; \
     pushl $0x23; \
     pushl %eax; \
     pushf; \
     pop %eax; \
     or %eax, $0x200; \
     push %eax; \
     pushl $0x1B; \
     push $1f; \
     iret; \
     1:");*/
     
     return;
}

//Make POSIX
int start_thread(task_t *thread, int attr, void (*func)(void*), void *arg)
{
	if (new_thread() == 0)
	{
		func(arg);
		exit();
		for(;;);
	}
	kill_thread(thread);
	return 1;
}

int kill_thread(task_t *thread)
{
	/*
	task_t *task_d = 0;
	task_t *task_r = (task_t*)ready_queue;
	for(;task_r->next!=0;task_r=task_r->next)
	{
		if(task_r->pid == thread->pid && task_r->thread == thread->thread)
			task_d=task_r;
	}
	if(!task_d)
		return 1;
	task_r = task_d->next;
	task_d->next = task_r->next;
          
	kfree(task_r);   
	*/
	return 0;
}

// Linking new task
int LinkTask (task_t * newtask)
{
	task_t *tmp_task = current_task;
	if (tmp_task == NULL)
	{
		kprintf ("Error!!! Function \"LinkTask\".\n");
		return 1;
	}
	if (tmp_task->child == NULL)
	{
		tmp_task->child = newtask;
		newtask->parent = tmp_task;
	}
	else
	{
		tmp_task = tmp_task->child;
		while (tmp_task->brother) tmp_task = tmp_task->brother;
		tmp_task->brother = newtask;
		newtask->parent = tmp_task->parent;
	}
	return 0;
}

// Unlinking task
int UnlinkTask (task_t *task)
{
	task_t * parent;
	//set_task_status (task, TASK_DEAD);
	if (task == NULL)
	{
		dprintf ("[UnlinkTask] Error!!! Null pointer...\n");
		return 1;
	}
	parent = task->parent;
	if (parent == 0 && task->brother)
	{
		dprintf ("[UnlinkTask] Error!!! Cannot find parent or brother! This action cannot be done...\n");
		return 2;
	}
	if (parent->child == task)
	{
		parent->child = task->brother;
		task->brother = NULL;
		task->parent = NULL;
	}
	else
	{
		parent = parent->child;
		while (parent->brother)
		{
			if (parent->brother == task)
				break;
			parent = parent->brother;
		}
		if (parent->brother != task)
		{
			dprintf ("[UnlinkTask] Error!!! Cannot find this task! System error...\n");
			return 3;
		}
		parent->brother = task->brother;
		task->brother = NULL;
		task->parent = NULL;
	}
	return 0;
}

static void DeleteTask (task_t * task)
{
	uint32 flags;
	save_flags (flags);
	cli ();
	if (task->child)
		DeleteTask (task->child);
	push_pid (task->pid);
	
	task_t * btmp = NULL;
	task_t * tmp = ready_queue;
	while (tmp)
	{
		if (tmp->pid == task->pid)
		{
			if (tmp->pid == ready_queue->pid)
				ready_queue = ready_queue->next;
			else
				btmp->next = tmp->next;
			break;
		}
		btmp = tmp;
		tmp = tmp->next;
	}
	
	destroy_directory(task->page_directory);
	
	while (task->threads)
	{
		thread_t *tmp = task->threads->next;
		delete_tty (task->threads);
		if (task->threads->terminal)
			kfree (task->threads->terminal);
		kfree (task->threads);
		task->threads = tmp;
	}
	kfree(task);
	restore_flags (flags);
}

static task_t * FindTask (task_t * task, pid_t pid)
{
	int ret = 0;
	if (task->child)
		ret = FindTask (task->child,pid);
	if (ret)
		return ret;
	if (task->brother)
		ret = FindTask (task->brother, pid);
	if (ret)
		return ret;
	if (task->pid == pid)
		return task;
	return 0;
}

static task_t * FindThread (task_t * task, pid_t tid)
{
	int ret = 0;
	if (task->child)
		ret = FindTask (task->child, tid);
	if (ret)
		return ret;
	if (task->brother)
		ret = FindTask (task->brother, tid);
	if (ret)
		return ret;
	thread_t * tmp = task->threads;
	while (tmp)
	{
		if (tmp->tid == tid)
			return tmp;
		tmp = tmp->next;
	}
	return 0;
}

thread_t * GetThread (tid_t tid)
{
	task_t * tmp = current_task;
	while (tmp->parent) tmp = tmp->parent;
	return FindThread (tmp,tid);
}

task_t * GetTask (pid_t pid)
{
	task_t * tmp = current_task;
	while (tmp->parent) tmp = tmp->parent;
	return FindTask (tmp,pid);
}

void task_control(pid_t pid, state_t state)
{
	task_t * tmp = current_task;
	while (tmp->parent) tmp = tmp->parent;
	thread_t * thread_s = FindThread (tmp,(tid_t)pid);
	if (!thread_s)
		return;
	if (thread_s->tid == thread_s->parent->pid)
		thread_s->parent->state = state;
	thread_s->state = state;
}

extern bool was_switch_task;

void switch_task()
{
	const pid_t init_pid = 100;
	static pid_t last_pid = 100;
	// Если нет инициализированных задач, выхожу
	if (!current_task)
		return;
	// Теперь читаю esp, ebp для того, что бы их сохранить
	uint32 esp, ebp, eip;
	
	asm volatile("mov %%esp, %0" : "=r"(esp));
	asm volatile("mov %%ebp, %0" : "=r"(ebp));

	// Читаю указатель инструкции.Здесь использую сложную логику:
	// Когда происходит вызов этой функции, то возможен один из двух случаев:
	// 1. Вызвал функцию и она вернула EIP.
	// 2. Только что переключил задачи и поскольку сохраненым значением EIP
	// В сущности является инструкция, идущая за read_eip(), то будет все выглядеть так,
	// как если бы только что произошел выход из функции read_eip
	// Во втором случае необходимо немедленно выйти из функции. Что бы обнаружить эту ситуацию,
	// я помещаю в eax фиктивное значение, которое будет проверяться в конце работы функции
	// Поскольку в языке C регистр EAX используется для возврата значений, будет
	// выглядеть так, как будто бы возвращаемым значением будет это фиктивное значение! (0x12345).
	eip = read_eip();

	// Только что выполнено переключение задач?
	if (eip == 0x12345)
		return;

	if (current_task->pid != init_pid || (current_task->pid == init_pid && init_pid == last_pid))
	{
		// Нет, переключение задач не выполнено. Сохраняю значения регистров и выполняю переключение.
		current_task->cur_thread->eip = eip;
		current_task->esp = esp;
		current_task->ebp = ebp;
	}
	
	if (!current_task->cur_thread->next)
	{
		current_task->cur_thread = current_task->threads;
		//task_t * tmp = GetNext ();
		//if (tmp == NULL)
		//{
		//	dprintf ("[switch_task] System error! System had not proccesses!!!\n");
		//	return;
		//}
		if (current_task->cur_thread->state != TASK_WAITING)
			current_task->state = current_task->cur_thread->state = TASK_SLEEPING;
		if (current_task->cur_thread->state == TASK_WAITING && current_task->state != TASK_WAITING
				&& current_task->cur_thread == current_task->threads && current_task->threads->next == NULL)
			current_task->state = TASK_WAITING;
		//current_task = tmp;
		// Get the next task to run.
  		current_task = current_task->next;
		// If we fell off the end of the linked list start again at the beginning.
		if (!current_task) current_task = ready_queue;
	}
	else
	{
		if (current_task->cur_thread->state != TASK_WAITING)
			current_task->state = current_task->cur_thread->state = TASK_SLEEPING;
		
		if (current_task->cur_thread->state == TASK_WAITING && current_task->state != TASK_WAITING
				&& current_task->cur_thread == current_task->threads && current_task->threads->next == NULL)
			current_task->state = TASK_WAITING;
		current_task->cur_thread = current_task->cur_thread->next;
	}
	if (current_task->cur_thread->state != TASK_WAITING)
			current_task->state = current_task->cur_thread->state = TASK_RUNNING;
	if (current_task->cur_thread->state == TASK_WAITING && current_task->state != TASK_WAITING
			&& current_task->cur_thread == current_task->threads && current_task->threads->next == NULL)
		current_task->state = TASK_WAITING;
	// Make sure the memory manager knows we've changed page directory.
	current_directory = current_task->page_directory;
	
	eip = current_task->cur_thread->eip;
	esp = current_task->esp;
	ebp = current_task->ebp;
	last_pid = current_task->pid;
	
	//dprintf ("[switch_task] esp = 0x%x ebp = 0x%x\n",esp,ebp);
	//dprintf ("[switch_task] eip = 0x%x cd = 0x%x\n",eip,current_directory);
	//DBGPRINTF(' ');
	//DBGPRINTF(current_task->pid);
		
	//Проверка флага IF
	//int IF = 1;
	//asm volatile ("			\
	//		pushf;			\

	//		pop %%eax;		\
	//		and $0x200, %%eax;	\
	//		mov %%eax, %0" : "=r"(IF));
	//
	//if (!IF)
	//	printf ("IF not activated!\n");
	//else
	//	printf ("IF activated!\n");
		
	// Здесь:
	// * Останавливаю прерывания
	// * Временно помещаю значение нового положения eip в регистр ecx
	// * Загружаю указатели стека и базы из структуры task новой задачи
	// * Заменяю указатель директория страниц на физический адрес (physicalAddr) нового директория.
	// * Помещаю в регистр EAX фиктивное значение (0x12345) с тем, чтобы мог его сразу опознать в  
	// случае, кода выполню переключение задач.
	// * Снова запускаю прерывания. В инструкции STI будет задержка — она не срабатывает до тех пор, 
	// пока не произойдет переход к новой инструкции.
	// * Перехожу на позицию, указываемую в ECX (сюда я поместил новое значение EIP).
	
	was_switch_task = true;
	asm volatile("			\
		cli;				\
		mov %0, %%ecx;		\
		mov %1, %%esp;		\
		mov %2, %%ebp;		\
		mov %3, %%cr3;		\
		mov $0x12345, %%eax;\
		sti;				\
		jmp *%%ecx           "
			: : "r"(eip), "r"(esp), "r"(ebp), "r"(current_directory->physicalAddr));
}

void key_switch_task()
{
	// Если нет инициализированных задач, выхожу
	if (!current_task)
		return;
	// Теперь читаю esp, ebp для того, что бы их сохранить
	uint32 esp, ebp, eip;
	asm volatile("mov %%esp, %0" : "=r"(esp));
	asm volatile("mov %%ebp, %0" : "=r"(ebp));

	// Читаю указатель инструкции.Здесь использую сложную логику:
	// Когда происходит вызов этой функции, то возможен один из двух случаев:
	// 1. Вызвал функцию и она вернула EIP.
	// 2. Только что переключил задачи и поскольку сохраненым значением EIP
	// В сущности является инструкция, идущая за read_eip(), то будет все выглядеть так,
	// как если бы только что произошел выход из функции read_eip
	// Во втором случае необходимо немедленно выйти из функции. Что бы обнаружить эту ситуацию,
	// я помещаю в eax фиктивное значение, которое будет проверяться в конце работы функции
	// Поскольку в языке C регистр EAX используется для возврата значений, будет
	// выглядеть так, как будто бы возвращаемым значением будет это фиктивное значение! (0x12345).
	eip = read_eip();

	// Только что выполнено переключение задач?
	if (eip == 0x12345)
		return;
	
	//uint32 flags;
	//save_flags (flags);
	//cli ();
	if (current_task->cur_thread->terminal)
	{
		if (!current_task->cur_thread->terminal->busy_keyb)
			return;
	}
	task_t * ptmp = (task_t*) ready_queue;
	thread_t * ttmp = NULL;
	while (ptmp)
	{
		ttmp = ptmp->threads;
		while (ttmp)
		{
			if (ttmp->terminal)
			{
				break;
			}
			ttmp = ttmp->next;
		}
		ptmp = ptmp->next;
	}
	if (!ttmp)
	{
		//restore_flags (flags);
		return;
	}

	while (ttmp->terminal->busy_keyb)
	{
		ttmp = ttmp->terminal->busy_keyb;
	}

	if (!ttmp)
	{
		//restore_flags (flags);
		return;
	}

	// Нет, переключение задач не выполнено. Сохраняю значения регистров и выполняю переключение.
	current_task->cur_thread->eip = eip;
	current_task->esp = esp;
	current_task->ebp = ebp;
	
	current_task->state = current_task->cur_thread->state = TASK_SLEEPING;
	
	current_task = ttmp->parent;
	current_task->cur_thread = ttmp;
	
	current_task->state = current_task->cur_thread->state = TASK_RUNNING;
	// Make sure the memory manager knows we've changed page directory.
	current_directory = current_task->page_directory;
	
	eip = current_task->cur_thread->eip;
	esp = current_task->esp;
	ebp = current_task->ebp;
	
	//dprintf ("[switch_task] esp = 0x%x ebp = 0x%x\n",esp,ebp);
	//dprintf ("[switch_task] eip = 0x%x cd = 0x%x\n",eip,current_directory);
	//DBGPRINTF(' ');
	//DBGPRINTF(current_task->pid);
		
	// Здесь:
	// * Останавливаю прерывания
	// * Временно помещаю значение нового положения eip в регистр ecx
	// * Загружаю указатели стека и базы из структуры task новой задачи
	// * Заменяю указатель директория страниц на физический адрес (physicalAddr) нового директория.
	// * Помещаю в регистр EAX фиктивное значение (0x12345) с тем, чтобы мог его сразу опознать в  
	// случае, кода выполню переключение задач.
	// * Снова запускаю прерывания. В инструкции STI будет задержка — она не срабатывает до тех пор, 
	// пока не произойдет переход к новой инструкции.
	// * Перехожу на позицию, указываемую в ECX (сюда я поместил новое значение EIP).
	
	//restore_flags (flags);
	
	was_switch_task = true;
	asm volatile("			\
		cli;				\
		mov %0, %%ecx;		\
		mov %1, %%esp;		\
		mov %2, %%ebp;		\
		mov %3, %%cr3;		\
		mov $0x12345, %%eax;\
		sti;				\
		jmp *%%ecx           "
			: : "r"(eip), "r"(esp), "r"(ebp), "r"(current_directory->physicalAddr));
}

int register_user (char * user, int uid)
{
	uint32 flags;
	save_flags (flags);
	cli();
    
	task_t *parent_task = (task_t*)current_task;

	// clone directory space
	page_directory_t *directory = clone_directory(current_directory);
	
	task_t *new_user = CreateTaskStruct (user,(pid_t)uid, directory, PRIO_NORMAL);
	new_user->pid = uid;
	new_user->uid = uid;
	
	new_user->threads->tid = uid;
	new_user->cur_thread = new_user->threads;
	create_tty (new_user->cur_thread,NULL);

	// add to linked list
	task_t *tmp_task = (task_t*)ready_queue;
	while (tmp_task->next)
		tmp_task = tmp_task->next;
	tmp_task->next = new_user;
	
	// Добавляю в древо процессов
	LinkTask (new_user);

	// entry point
	uint32 eip = read_eip();

	if (current_task == parent_task)
	{
		// We are the parent, so set up the esp/ebp/eip for our child.
		uint32 esp;
		uint32 ebp;
		asm volatile ("mov %%ebp, %0" : "=r"(ebp));
		asm volatile ("mov %%esp, %0" : "=r"(esp));
		new_user->esp = esp;
		new_user->ebp = ebp;
		new_user->cur_thread->eip = eip;
		restore_flags (flags);
		
		save_return_point (ready_queue);
		ready_queue = new_user;

		return new_user->pid;
	}
	else
	{
		// We are the child.
		return -3;
	}
}

pid_t get_current_pid ()
{
	return current_task->pid;
}